import axios, { type AxiosRequestConfig } from 'axios'
import qs from 'qs'
import router from '@/router'
import { message } from 'ant-design-vue'

const http = axios.create({
  baseURL: import.meta.env.VITE_BASE_URL,
  timeout: 10 * 1000, //10s
  paramsSerializer(params: any) {
    return qs.stringify(params, { arrayFormat: 'repeat' })
  },
})

const pendingSet = new Set() //请求集合
http.interceptors.request.use(
  (config: any) => {
    if (!config.allow_repeat_request) {
      addPending(config) //避免重复请求
    }

    const token = sessionStorage.getItem('token') ?? ''
    config.headers = {
      ...config.headers,
      authorization: 'Bearer ' + token,
      token,
    } as any

    return config
  },
  error => {
    clearPendingSet()
    return Promise.reject(error)
  }
)

http.interceptors.response.use(
  response => {
    removePending(response.config)
    if (response.status === 200) {
      const { code, msg } = response.data ?? {}
      const { return_initial_response, close_error_message } = response.config as RequestConfig

      if (return_initial_response) {
        //返回原始结果
        return response
      }

      if (code === 200) {
        return response.data
      }
      if (code === 401) {
        message.error(msg || '未登录，或登录已经超时')
        router.replace('/login')
      }
      if (code === 500) {
        if (!close_error_message) {
          message.error(msg || '服务异常')
        }
        return response.data
      }

      return response.data
    } else {
      return Promise.reject(response)
    }
  },
  error => {
    // 处理被取消的请求
    if (axios.isCancel(error)) {
      error.message && message.info(error.message)
      return Promise.reject(error)
    }

    let msg = ''
    const msgMap: any = {
      302: '接口重定向了',
      400: '参数不正确',
      401: '未登录，或者登录已经超时',
      403: '没有权限',
      404: '请求地址不存在',
      408: '请求超时',
      500: '服务器内部错误',
    }

    if (error && error.response) {
      msg = msgMap[error.response.status]
    }
    if (error.message.includes('timeout')) {
      msg = '请求超时'
    }
    if (error.message.includes('Network')) {
      msg = window.navigator.onLine ? '服务异常' : '网络异常'
    }

    message.error(msg || '服务异常')
    clearPendingSet()
    return Promise.reject(error)
  }
)

interface RequestConfig extends AxiosRequestConfig {
  close_error_message?: boolean //是否关闭错误提示
  return_initial_response?: boolean //是否返回原始结果
  allow_repeat_request?: boolean //是否允许重复请求
}

//默认配置
const defaultConfig = {
  close_error_message: false,
  return_initial_response: false,
  allow_repeat_request: false,
}

const request = (requestConfig: RequestConfig) => {
  return http({ ...defaultConfig, ...requestConfig })
}

//便捷请求
const simpleRequest = (requestConfig: Omit<RequestConfig, 'return_initial_response'>) => {
  return new Promise((resolve, reject) => {
    http({ ...defaultConfig, ...requestConfig }).then((res: any) => {
      const { code, data } = (res as { code: number; data: any; msg: string }) || {}
      if (code === 200) {
        resolve(data)
      } else {
        reject(res)
      }
    })
  })
}

//带取消的请求
const requestWithCancel = (requestConfig: RequestConfig) => {
  const source = axios.CancelToken.source()
  const request = http({
    cancelToken: source.token,
    ...defaultConfig,
    ...requestConfig,
  })
  return {
    cancel(msg = '') {
      source.cancel(msg)
    },
    request,
  }
}

//生成请求标志
function getPendingKey(config: RequestConfig) {
  let { url, method, params, data } = config
  if (typeof data === 'string') data = JSON.parse(data)
  return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}
//添加标志到集合，并取消重复请求
function addPending(config: RequestConfig) {
  const pendingKey = getPendingKey(config)
  if (pendingSet.has(pendingKey)) {
    config.cancelToken = new axios.CancelToken(cancel => {
      cancel('')
    })
  } else {
    pendingSet.add(pendingKey)
  }
}

function removePending(config: RequestConfig) {
  const pendingKey = getPendingKey(config)
  pendingSet.delete(pendingKey)
}

function clearPendingSet() {
  pendingSet.clear()
}

export { request, simpleRequest, requestWithCancel, clearPendingSet }
